package adminlte.auth;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.util.matcher.OrRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.web.filter.GenericFilterBean;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Map;

/**
 * https://segmentfault.com/a/1190000014371789 未完成
 * @author ZHUFEIFEI
 */
@Slf4j
//@Component
public class IntegrationAuthenticationFilter extends GenericFilterBean implements ApplicationContextAware{

    private static final String AUTH_TYPE_PARAM_NAME = "auth_type";
    private static final String LOGIN_URL = "/login";
    private static final String LOGIN_URL_API = "/api/login";

    private RequestMatcher requestMatcher;
    private Collection<IntegrationAuthenticator> authenticators;
    private ApplicationContext applicationContext;

    public IntegrationAuthenticationFilter() {
        this.requestMatcher = new OrRequestMatcher(
                new AntPathRequestMatcher(LOGIN_URL, "GET"),
                new AntPathRequestMatcher(LOGIN_URL, "POST"),
                new AntPathRequestMatcher(LOGIN_URL_API, "GET"),
                new AntPathRequestMatcher(LOGIN_URL_API, "POST")
        );
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest req = (HttpServletRequest) request;
        HttpServletResponse resp = (HttpServletResponse) response;

        if (this.requestMatcher.matches(req)) {
            IntegrationAuthenticationContext.set(IntegrationAuthentication.builder()
                    .authType(req.getParameter(AUTH_TYPE_PARAM_NAME))
                    .authParameters(req.getParameterMap())
                    .build());

            try {
                this.prepare(IntegrationAuthenticationContext.get());

                chain.doFilter(request, response);

                this.complete(IntegrationAuthenticationContext.get());
            } finally {
                IntegrationAuthenticationContext.remove();
            }
        }

        chain.doFilter(request, response);
    }

    private void prepare(IntegrationAuthentication authentication) {
        if (this.authenticators == null) {
            synchronized (this) {
                Map<String, IntegrationAuthenticator> authenticatorMap = this.applicationContext.getBeansOfType(IntegrationAuthenticator.class);
                if (authenticatorMap != null) {
                    this.authenticators = authenticatorMap.values();
                }
            }
        }

        if(this.authenticators == null){
            this.authenticators = new ArrayList<>();
        }

        for (IntegrationAuthenticator authenticator: this.authenticators) {
            if(authenticator.support(authentication)){
                authenticator.prepare(authentication);
            }
        }
    }


    private void complete(IntegrationAuthentication authentication) {
        for (IntegrationAuthenticator authenticator : this.authenticators) {
            if (authenticator.support(authentication)) {
                authenticator.complete(authentication);
            }
        }
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
}
